# -*- coding: utf-8 -*-
import logging
import os
import json
import socket
import time
# import matlab.engine
import random
from app.utils.loaddata import LoadData
from app.utils.predictOneLoaddata import PredictOneLoadData
from flask import Blueprint, request, current_app
import time
import ast
import pandas as pd
from app.models.model import MaterialInfo, PredictTask, TrainingInfo,TrainingModelInfo,PublishAPIServer,Project,PublishTaskData
from app.utils.response import ResMsg
from app.utils.util import route
from app.utils.ResponseUtil import ResponseCode
from app.utils.exception import TaskParamsFormatError, MenUsedUpToMaxError, TaskHasExistError, PredictParamsFormatError, \
    TaskNotFoundError, TaskInitError, PredictParamsNotFoundDiscreteError, DataBaseError, PredcitError
api_ml = Blueprint("api", __name__, url_prefix='/api')
logger = logging.getLogger(__name__) # 返回一个新的以文件名为名的logger



@route(api_ml, '/create', methods=["POST"])
def create():
    SCP_USE = current_app.config.get('SCP_USE')
    BIND = current_app.config.get('BIND')
    '''
    创建API接口，处理传过来的参数，生成API
    传入参数json格式应该为
    {
        'task_id': 任务名称,
        'task_type': 任务类型
        # 'file_id': 素材id
        'model_name': 模型名称,
        'variable_name': 变量参数,
        'date_name': 时间变量,
        'maps': 映射关系对应参数, 结构为{ 'label': {}, 'feature': {}}, label代表标签， feature代表特征
    }
    :return:
    '''

    logger.info("开始调用接口", exc_info=True)
    params = json.loads(request.get_data(as_text=True))
    logger.info(params,exc_info=True)
    task_id = str(params['task_id'])
    db = current_app.extensions["machine_state"].db ###新增
    publish_list = db.session.query(PublishTaskData).filter(PublishTaskData.id == task_id).all()
    publish_task = publish_list[0]
    project_id = publish_task.project_id ###新增

    project_task = db.session.query(Project).filter(Project.id == project_id).all()[0]
    project_task.status = "publishing"
    db.session.commit()###新增


    if 'task_id' not in params:
        logger.error("参数无效, task_id为空或不存在", exc_info=True)
        res = ResMsg(code=ResponseCode.InvalidParameter, data="task_id 不存在或不能为空")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data

    if 'model_name' not in params or params['model_name'].strip() == '':
        logger.error("参数无效, model_name为空或不存在", exc_info=True)
        res = ResMsg(code=ResponseCode.InvalidParameter, data="model_name 不存在或不能为空")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data

    # if 'model_url' not in params or params['model_url'].strip() == '':
    #     logger.error("参数无效, model_url为空或不存在");
    #     res = ResMsg(code=ResponseCode.InvalidParameter, data="model_url 不存在或不能为空");
    #     return res.data

    if 'variable_name' not in params:
        logger.error("参数无效, variable_name为空或者不存在", exc_info=True)
        res = ResMsg(code=ResponseCode.InvalidParameter, data="variable_name为空或者不存在")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data

    if 'task_type' not in params:
        logger.error("参数无效, task_type为空或者不存在", exc_info=True)
        res = ResMsg(code=ResponseCode.InvalidParameter, data="task_type为空或者不存在")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data

    if 'maps' not in params:
        logger.error("参数无效, map为不存在，如果不需要用到map，请填写{'label': {}, 'feature': {}}", exc_info=True)
        res = ResMsg(code=ResponseCode.InvalidParameter, data="map为不存在，如果不需要用到map，请填写{'label': {}, 'feature': {}}")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data


    task_type = params['task_type']
    task_type = str(task_type)
    model_name = params['model_name']
    variable_name = params["variable_name"]
    date_name = None
    if 'date_name' in params:
        date_name = params['date_name']

    map = params["maps"]
    labels = map['label']
    feature = map['feature']

    # if 'file_id' not in params:
    #     logger.error("参数无效, file_id为空或不存在", exc_info=True);
    #     res = ResMsg(code=ResponseCode.InvalidParameter, data="file_id 不存在或不能为空");
    #     return res.data
    # file_id = params['file_id']
    # db = current_app.extensions["machine_state"].db
    # try:
    #     if db is not None:
    #         task_list = db.session.query(MaterialInfo).filter(MaterialInfo.id == file_id).all()
    #         if task_list is None or len(task_list) != 1:
    #             logger.info(file_id + "的素材不存在或者重复多条")
    #             res = ResMsg(code=ResponseCode.DataBaseError, data=file_id + "的素材不存在或者重复多条");
    #             return res.data
    #         bucket = task_list[0].bucket_dir
    #     else:
    #         logger.info("数据库信息为空," + file_id +"的素材查找失败")
    #         res = ResMsg(code=ResponseCode.DataBaseError, data=file_id + "的素材查找失败");
    #         return res.data
    # except Exception as e:
    #     logging.error(e, exc_info=True);
    #     res = ResMsg(code=ResponseCode.DataBaseError, data=file_id + "的素材查找失败");
    #     return res.data

    # SCP_USE==0时从本地服务器读取数据，等于1时通过scp的方式从对应服务器拉取数据
    (filepath, modelfilename) = os.path.split(model_name)
    if(SCP_USE==0):
        model_abs_path = model_name
    else:
        model_abs_path = os.path.join(current_app.config["MODEL_PATH"], modelfilename)
        try:
            if not os.path.exists(model_abs_path):
                current_app.extensions["scp_client"].download(model_name, model_abs_path)
        except Exception as e:
            logging.error(e, exc_info=True)
            res = ResMsg(code=ResponseCode.DownloadResourceFail, data=modelfilename + "文件下载失败")
            project_task.status = "error"
            db.session.commit()  ###新增
            return res.data


    try:
        task_info = {
            "task_id": task_id,
            "task_type": task_type,
            "model_name": model_name,
            "model_path": model_abs_path,
            # "model_path": model_name,
            "variable_name": variable_name,
            "labels": labels,
            "feature": feature,
            "date_name": date_name
        }
        current_app.extensions["machine_state"].add_task(task_info)
    except TaskParamsFormatError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPICreateError, data=e.message)
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    except MenUsedUpToMaxError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPICreateError, data=e.message)
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    except TaskHasExistError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPICreateError, data=e.message)
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    except TaskInitError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPICreateError, data=e.message)
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    except DataBaseError as e:
        logger.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.DataBaseError, data=e.message)
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    except Exception as e:
        logger.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPICreateError, data="创建预测接口API失败")
        project_task.status = "error"
        db.session.commit()  ###新增
        return res.data
    res = ResMsg(code=ResponseCode.Success, data="创建预测接口API成功")

    try:
        ###获取服务器地址
        s = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
        s.connect(('10.0.0.1',8080))
        ip= s.getsockname()[0]

        ###更新数据库信息
        logging.info("%s正在更改任务状态"%str(task_id))
        publish_task.status = "published"
        db.session.commit()
        project_task.status = "published"
        db.session.commit()
        trainingmodelinfo = db.session.query(TrainingModelInfo).filter(TrainingModelInfo.project_id == project_id).all()[0]
        logging.info("项目ID%s"%str(trainingmodelinfo.project_id))
        trainingmodelinfo.status = "published"
        trainingmodelinfo.api = "http://%s:%s/api/%s/customPredict"%(str(ip),str(BIND),str(task_id))
        db.session.commit()
        logging.info("%s更改任务状态成功"%str(task_id))
    except Exception as e:
        logger.error(e.message, exc_info=True)

    return res.data


@route(api_ml, '/<int:task_id>/predict', methods=["POST"])
def predict(task_id):

    '''
    预测接口
    :param task_id: 任务id  
    :return:
    '''
    params = json.loads(request.get_data(as_text=True))

    if "data" not in params:
        res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data="没有data字段")
        return res.data
    try:
        predict_result = current_app.extensions["machine_state"].predict_task(task_id=task_id, predict_params=params["data"])
        res = ResMsg(code=ResponseCode.Success, data=predict_result)
        return res.data
    except PredictParamsFormatError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data=e.message)
        return res.data
    except TaskNotFoundError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPINotExist, data=e.message)
        return res.data
    except PredictParamsNotFoundDiscreteError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictParamsNotFoundDiscrete, data=e.message)
        return res.data
    except PredcitError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPIFailed, data=e.message)
    except Exception as e:
        logging.error(e, exc_info=True)
        message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式,或重新部署模型"
        res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
        return res.data


@route(api_ml, '/<int:task_id>/predictBig', methods=["POST"])
def predictBig(task_id):
    SCP_USE = current_app.config.get('SCP_USE')
    '''
    预测接口
    :param predictId: 预测任务id
    :return:
    '''
    params = json.loads(request.get_data(as_text=True))

    if "predictId" not in params:
        res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data="没有predictId字段")
        return res.data
    predict_id = params["predictId"]
    # 获取任务
    db = current_app.extensions["machine_state"].db
    predict_task = None
    try:
        if db is not None:
            predict_list = db.session.query(PredictTask).filter(PredictTask.id == predict_id).all()
            if predict_list is None or len(predict_list) != 1:
                logger.info(predict_id + "的预测任务不存在或者重复多条")
                res = ResMsg(code=ResponseCode.DataBaseError, data=predict_id + "的预测任务不存在或者重复多条")
                return res.data
            predict_task = predict_list[0]

            ###新增内容——12-29,查表找task_type，因为暂无外部参数传入tasktype，采用2次查表
            training_info_list = db.session.query(TrainingInfo).filter(TrainingInfo.project_id
                                                                       == predict_task.project_id).all()
            # 模型名称和模型id，后续需要用的参数。
            if training_info_list is not None and len(training_info_list) == 1:
                training_info = training_info_list[0]
                task_type = training_info.type
            else:
                ###新增内容——12-29,查表找task_type，因为暂无外部参数传入tasktype，采用2次查表
                import_model_info_list = db.session.query(TrainingModelInfo).filter(TrainingModelInfo.project_id
                                                                           == predict_task.project_id).all()
                # 模型名称和模型id，后续需要用的参数。
                if import_model_info_list is not None and len(import_model_info_list) == 1:
                    import_model_info = import_model_info_list[0]
                    task_type = import_model_info.model_file_type
                    model_dir = import_model_info.model_dir
                    (modelfilepath, modelfilename) = os.path.split(model_dir)
                    if (SCP_USE == 0):
                        model_abs_path = model_dir
                    else:
                        model_abs_path = os.path.join(current_app.config["MODEL_PATH"], modelfilename)

        else:
            logger.info("数据库信息为空," + predict_id +"的预测任务查找失败")
            res = ResMsg(code=ResponseCode.DataBaseError, data=predict_id + "的预测任务查找失败")
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predict_id + "的预测任务查找失败")
            return res.data
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.DataBaseError, data=predict_id + "的预测任务任务查找失败")
        current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predict_id + "的预测任务查找失败")
        return res.data

    predict_file = predict_task.predict_file

    # task_id = predict_task.publish_id

    # SCP_USE==0时从本地服务器读取数据，等于1时通过scp的方式从对应服务器拉取数据
    (filepath, predictfilename) = os.path.split(predict_file)
    if (SCP_USE==0):
        data_abs_path = predict_file
    else:
        data_abs_path = os.path.join(current_app.config["DATA_PATH"], predictfilename)
        try:
            current_app.extensions["scp_client"].download(predict_file, data_abs_path)
        except Exception as e:
            logging.error(e, exc_info=True)
            res = ResMsg(code=ResponseCode.DownloadResourceFail, data=predictfilename + "预测文件下载失败")
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predictfilename + "预测文件下载失败")
            return res.data

    #防止文件未下载完成就开始预测
    while not os.path.exists(data_abs_path):
        time.sleep(1)
    # 2、读取当前目录下的数据
    # data_abs_path = predict_file
    data = None
    try:
        if task_type == 'matlab':
            # 解压并获取脚本路径和模型路径
            matlab_file, model_file = LoadData.get_local_data(model_abs_path, task_type=task_type)
        else:
            # 读取文件整合在utils.loaddata，所以需要添加判别类型参数
            logging.info(data_abs_path)
            data, file_name_df = LoadData.get_local_data(data_abs_path, encoding='utf-8',task_type=task_type)
            jsondata = data.to_json(orient='records')
            newdata = json.loads(jsondata)
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.ReadResourceFail, data=predictfilename + "预测文件读取失败")
        current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predictfilename + "预测文件读取失败")
        return res.data

    # 更新任务状态
    try:
        # predictTask = db.session.query(PredictTask).filter(PredictTask.id == predict_id).first()
        predict_task.status = "running"
        db.session.commit()
    except Exception as e:
        current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error",
                                                                    error="数据库出问题了，保存预测信息失败")
        raise DataBaseError("数据库出问题了，保存预测信息失败")

    if task_type == 'matlab':
        # eng = matlab.engine.start_matlab()
        # # 进入.m文件目录
        # matlab_dir, matlab_file_name = os.path.split(matlab_file)
        # eng.cd(matlab_dir, nargout=0)
        # # 执行.m文件
        # # result_abs_path = eng.triarea('RF.mat', 'test.csv')
        # result_abs_path = eng.triarea(model_file, data_abs_path)
        # eng.quit()
        matlab_dir, matlab_file_name = os.path.split(matlab_file)
        matlab_name = os.path.splitext(matlab_file_name)[0]
        result_file_name = str(predict_id) + str(task_id) + '.csv'
        result_abs_path = None
        if (SCP_USE == 0):
            result_abs_path = filepath + "/" + result_file_name
        else:
            result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
        # cmd1 = "cd /opt/zjw/matlab"
        # cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"triarea('RF.mat','/opt/zjw/testdata.csv','/opt/zjw/result.csv')\"
        cmd1 = "cd "+matlab_dir
        cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"" + matlab_name + "('" + os.path.basename(model_file) + "','" + os.path.abspath(data_abs_path) + "','" + os.path.abspath(result_abs_path) + "')\""
        cmd = cmd1 + "&&" + cmd2
        flag = os.system(cmd)
        if flag != 0:
            logging.error("matlab预测脚本执行失败", exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式和脚本文件"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task,
                                                                        status="error", error=message)
            return res.data
    else:
        # 3、进行预测
        try:
            predict_result = current_app.extensions["machine_state"].predict_task(task_id=task_id, predict_params=newdata)
        except PredictParamsFormatError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except TaskNotFoundError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPINotExist, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except PredictParamsNotFoundDiscreteError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictParamsNotFoundDiscrete, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except PredcitError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except Exception as e:
            logging.error(e, exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式,或重新部署模型"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=message)
            return res.data

        # 本地写入结果数据、从本地上传数据到MinIO
        result_file_name = str(predict_id) + str(task_id) + '.csv'
        result_abs_path = None
        if(SCP_USE==0):
            result_abs_path = filepath + "/" + result_file_name
        else:
            result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
        # 4、结果写入本地——输出文件
        if task_type in ['1001','1002','1003','1004','1005','1006','sklearn']:
            data['result'] = predict_result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                data.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predictfilename + "预测结果文件写入失败")
                return res.data
        elif task_type in ['1007','1008']:   #1007和1008输出的是文件名+result
            file_name_df['result'] = predict_result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                file_name_df.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task,
                                                                            status="error", error=predictfilename + "预测结果文件写入失败")
                return res.data

        elif task_type in ('1009','1010'):   #1007和1008输出的是文件名+result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                predict_result.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
                predict_result = '文件写入成功'
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task,
                                                                            status="error", error=predictfilename + "预测结果文件写入失败")
                return res.data

    # # 5、从本地上传数据
    result_file = None
    if(SCP_USE==0):
        result_file = result_abs_path
    else:
        result_file = filepath + "/" + result_file_name
        try:
            current_app.extensions["scp_client"].upload(result_file, result_abs_path)
        except Exception as e:
            logging.error(e, exc_info=True)
            res = ResMsg(code=ResponseCode.UploadResourceFail, data=predictfilename + "预测文件上传失败")
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task,
                                                                        status="error", error=predictfilename + "预测文件上传失败")
            return res.data

    # 6、保存信息到数据库
    try:
        # predictTask = db.session.query(PredictTask).filter(PredictTask.id == predict_id).first()
        predict_task.status = "complete"
        predict_task.end_time = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime())
        predict_task.error = "预测成功"
        predict_task.result_file = result_file
        # predict_task.result_file = result_abs_path
        if data is not None:
            predict_task.total_row_data = data.shape[0]
        predict_task.predict_precent = 1
        db.session.commit()
    except Exception as e:
        current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task,
                                                                    status="error", error="数据库出问题了，保存预测信息失败")
        raise DataBaseError("数据库出问题了，保存预测信息失败")

    res = ResMsg(code=ResponseCode.Success, data="预测成功")
    return res.data


@route(api_ml, '/<int:task_id>/stop', methods=['GET'])
def stop(task_id):
    try:
        current_app.extensions["machine_state"].stop_task(str(task_id))
        logging.info("task_id: " + str(task_id) + " 的预测接口暂停成功")
        res = ResMsg(code=ResponseCode.Success, data="task_id: " + str(task_id) + " 的预测接口暂停成功")
        return res.data
    except TaskNotFoundError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPINotExist, data=e.message)
        return res.data


@route(api_ml, '/<int:task_id>/recreate', methods=['POST'])
def restart(task_id):
    """
    重启API服务, 使用POST方法的
    {
        'model_name': 模型名称, 非必须的
        'is_new_model': 是否更新模型， 非必须的
        'variable_name': 变量参数
        'maps': 标签参数
    }
    :param task_id: 任务id
    :return:
    """
    SCP_USE = current_app.config.get('SCP_USE')
    params = json.loads(request.get_data(as_text=True))

    model_name = None
    model_abs_path = None
    if 'is_new_model' in params and params['is_new_model'] is True:
        try:
            if 'model_name' not in params or params['model_name'].strip() == '':
                logger.error("选择了更新模型, 所以model_name不能空或不存在")
                res = ResMsg(code=ResponseCode.InvalidParameter, data="选择了更新模型, 所以model_name不能空或不存在")
                return res.data

            model_name = params["model_name"]
            (filepath, modelfilename) = os.path.split(model_name)
            if SCP_USE==0:
                model_abs_path = model_name
            else:
                model_abs_path = os.path.join(current_app.config["MODEL_PATH"], modelfilename)
                current_app.extensions["scp_client"].download(model_name, model_abs_path)
        except Exception as e:
            logging.error(e, exc_info=True)
            res = ResMsg(code=ResponseCode.DownloadResourceFail, data=modelfilename + "文件下载失败");
            return res.data

    variable_name = None
    if 'variable_name' in params:
        variable_name = params['variable_name']

    labels = None
    feature = None
    if 'maps' in params:
        map = params['maps']
        if 'labels' in map:
            labels = map['labels']

        if 'feature' in map:
            feature = map['feature']

    try:
        if 'is_new_model' in params and params['is_new_model'] is True:
            current_app.extensions["machine_state"].recreate(task_id=str(task_id), model_name=model_name, model_path=model_abs_path, \
                                                             variable_name=variable_name, labels=labels, feature=feature)
        else:
            current_app.extensions["machine_state"].recreate(task_id=str(task_id), variable_name=variable_name, labels=labels, feature=feature)
    except TaskNotFoundError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPIReCreateError, data=e.message)
        return res.data
    except TaskInitError as e:
        logging.error(e.message, exc_info=True)
        res = ResMsg(code=ResponseCode.PredictAPIReCreateError, data=e.message)
        return res.data

    res = ResMsg(code=ResponseCode.Success, data="task_id: "+str(task_id)+" 重启预测接口成功")
    return res.data


@route(api_ml, '/getTaskList', methods=['GET', 'POST'])
def get_task_list():
    """
    获取服务器存在的任务
    :return:
    """
    task_list = current_app.extensions["machine_state"].get_task_list()
    res = ResMsg(code=ResponseCode.Success, data=task_list)
    return res.data




@route(api_ml, '/<int:task_id>/customPredict', methods=["POST"])
def customPredict(task_id):
    SCP_USE = current_app.config.get('SCP_USE')
    '''
    预测接口
    :param predict_file: 预测任务文件
    :param type: 预测任务文件
    :return result: 预测结果
    '''
    # 随机变量名，可以作映射label
    def ranstr(num):
        # 猜猜变量名为啥叫 H
        H = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

        salt = ''
        for i in range(num):
            salt += random.choice(H)

        return salt

    # 获取文件信息，保存到本地
    file_type = request.form.get('type')
    file = request.files.get('file')  ##获取前端传输来的索引为file的文件
    file_name = ranstr(8) + '.' + file_type
    predict_file = os.path.join(current_app.config["MODEL_PATH"], file_name)
    file.save(predict_file)

    # 获取任务
    db = current_app.extensions["machine_state"].db
    predict_task = None
    try:
        if db is not None:
            logging.info(task_id)
            ## 新增内容——12-29,查表找task_type，因为暂无外部参数传入tasktype，采用2次查表
            publish_api_list = db.session.query(PublishAPIServer).filter(PublishAPIServer.task_id
                                                                         == task_id).all()
            ## 模型名称和模型id，后续需要用的参数。

            publish_api = publish_api_list[0]
            task_type = publish_api.task_type
            model_dir = publish_api.model_name
            (modelfilepath, modelfilename) = os.path.split(model_dir)
            if (SCP_USE == 0):
                model_abs_path = model_dir
            else:
                model_abs_path = os.path.join(current_app.config["MODEL_PATH"], modelfilename)

        else:
            logger.info("数据库信息为空," + task_id + "的预测任务查找失败")
            res = ResMsg(code=ResponseCode.DataBaseError, data=task_id + "的预测任务查找失败")
            return res.data
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.DataBaseError, data=task_id + "的预测任务任务查找失败")
        return res.data

    # task_id = predict_task.publish_id

    # SCP_USE==0时从本地服务器读取数据，等于1时通过scp的方式从对应服务器拉取数据
    (filepath, predictfilename) = os.path.split(predict_file)
    # if (SCP_USE==0):
    #     data_abs_path = predict_file
    # else:
    #     data_abs_path = os.path.join(current_app.config["DATA_PATH"], predictfilename)
    #     try:
    #         current_app.extensions["scp_client"].download(predict_file, data_abs_path)
    #     except Exception as e:
    #         logging.error(e, exc_info=True)
    #         res = ResMsg(code=ResponseCode.DownloadResourceFail, data=predictfilename + "预测文件下载失败")
    #         current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=predictfilename + "预测文件下载失败")
    #         return res.data

    # 2、读取当前目录下的数据
    data_abs_path = predict_file
    data = None
    try:
        if task_type == 'matlab':
            # 解压并获取脚本路径和模型路径
            matlab_file, model_file = LoadData.get_local_data(model_abs_path, task_type=task_type)
        else:
            # 读取文件整合在utils.loaddata，所以需要添加判别类型参数
            data, file_name_df = LoadData.get_local_data(data_abs_path, encoding='utf-8',task_type=task_type)
            jsondata = data.to_json(orient='records')
            newdata = json.loads(jsondata)
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.ReadResourceFail, data=predictfilename + "预测文件读取失败")
        return res.data


    if task_type == 'matlab':
        # eng = matlab.engine.start_matlab()
        # # 进入.m文件目录
        # matlab_dir, matlab_file_name = os.path.split(matlab_file)
        # eng.cd(matlab_dir, nargout=0)
        # # 执行.m文件
        # # result_abs_path = eng.triarea('RF.mat', 'test.csv')
        # result_abs_path = eng.triarea(model_file, data_abs_path)
        # eng.quit()
        matlab_dir, matlab_file_name = os.path.split(matlab_file)
        matlab_name = os.path.splitext(matlab_file_name)[0]
        result_file_name = str(random.randint(0,1000)) + str(task_id) + '.csv'
        result_abs_path = filepath + "/" + result_file_name
        # cmd1 = "cd /opt/zjw/matlab"
        # cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"triarea('RF.mat','/opt/zjw/testdata.csv','/opt/zjw/result.csv')\"
        cmd1 = "cd "+matlab_dir
        cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"" + matlab_name + "('" + os.path.basename(model_file) + "','" + os.path.abspath(data_abs_path) + "','" + os.path.abspath(result_abs_path) + "')\""
        cmd = cmd1 + "&&" + cmd2
        flag = os.system(cmd)
        if flag != 0:
            logging.error("matlab预测脚本执行失败", exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式和脚本文件"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            return res.data
    else:
        # 3、进行预测
        try:
            predict_result = current_app.extensions["machine_state"].predict_task(task_id=task_id, predict_params=newdata)
        except PredictParamsFormatError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except TaskNotFoundError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPINotExist, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except PredictParamsNotFoundDiscreteError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictParamsNotFoundDiscrete, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except PredcitError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=e.message)
            current_app.extensions["machine_state"].update_predict_task(db=db, predict_task=predict_task, status="error", error=e.message)
            return res.data
        except Exception as e:
            logging.error(e, exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式,或重新部署模型"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            return res.data

        # 本地写入结果数据、从本地上传数据到MinIO
        result_file_name = str(random.randint(0,1000))  + str(task_id) + '.csv'
        result_abs_path = filepath + "/" + result_file_name

        # 4、结果写入本地——输出文件
        if task_type in ['1001','1002','1003','1004','1005','1006','sklearn']:
            data['result'] = predict_result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                data.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                return res.data
        elif task_type in ['1007','1008']:   #1007和1008输出的是文件名+result
            file_name_df['result'] = predict_result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                file_name_df.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                return res.data

        elif task_type in ('1009','1010'):   #1007和1008输出的是文件名+result
            # result_file_name = str(predict_id) + str(task_id) + '.csv'
            # # result_abs_path = os.path.join(current_app.config["DATA_PATH"], result_file_name)
            # result_abs_path = filepath + "/" + result_file_name
            try:
                predict_result.to_csv(result_abs_path, index=False, encoding='utf_8_sig')
            except Exception as e:
                logging.error(e, exc_info=True)
                res = ResMsg(code=ResponseCode.WriteResultFail, data=predictfilename + "预测结果文件写入失败")
                return res.data

    if task_type in ['1001', '1002', '1003', '1004', '1005', '1006', 'sklearn']:
        logging.info(data.values)
        return json.dumps({'result': data.values.tolist()}, ensure_ascii=False)
    elif task_type in ['1007', '1008']:  # 1007和1008输出的是文件名+result
        logging.info(file_name_df.values)
        return json.dumps({'result': file_name_df.values.tolist()}, ensure_ascii=False)
    elif task_type in ('1009', '1010'):
        logging.info(predict_result.values)
        return json.dumps({'result':predict_result.values.tolist()},ensure_ascii=False)
    elif task_type == 'matlab':
        predict_result = pd.read_csv(result_abs_path,header=None)
        logging.info(predict_result.values)
        return json.dumps({'result':predict_result.values.tolist()},ensure_ascii=False)





@route(api_ml, '/<int:task_id>/customPredictOne', methods=["POST"])
def customPredictOne(task_id):
    SCP_USE = current_app.config.get('SCP_USE')
    '''
    预测接口
    选择文件输入则输输进参数（predict_file，type），选择数据预测（predict_data）
    :param predict_file: 预测任务文件
    :param type: 预测任务文件
    :param predict_data 预测数据
    :return result: 预测结果
    '''
    # 随机变量名，可以作映射label
    def ranstr(num):
        # 猜猜变量名为啥叫 H
        H = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'

        salt = ''
        for i in range(num):
            salt += random.choice(H)

        return salt

    # 获取任务
    db = current_app.extensions["machine_state"].db
    try:
        if db is not None:
            logging.info(task_id)
            ## 新增内容——12-29,查表找task_type，因为暂无外部参数传入tasktype，采用2次查表
            publish_api_list = db.session.query(PublishAPIServer).filter(PublishAPIServer.task_id
                                                                         == task_id).all()
            ## 模型名称和模型id，后续需要用的参数。

            publish_api = publish_api_list[0]
            task_type = publish_api.task_type
            model_dir = publish_api.model_name
            (modelfilepath, modelfilename) = os.path.split(model_dir)
            if (SCP_USE == 0):
                model_abs_path = model_dir
            else:
                model_abs_path = os.path.join(current_app.config["MODEL_PATH"], modelfilename)

        else:
            logger.info("数据库信息为空," + task_id + "的预测任务查找失败")
            res = ResMsg(code=ResponseCode.DataBaseError, data=task_id + "的预测任务查找失败")
            return res.data
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.DataBaseError, data=task_id + "的预测任务任务查找失败")
        return res.data

    # 获取文件信息保存或获取预测单条参数
    if task_type in ['1001', '1002', '1003', '1004', '1005', 'sklearn']:
        predict_data = ast.literal_eval(request.form.get('predict_data'))
    elif task_type == '1006':
        predict_data = request.form.get('txt',default=None,type=str)
    else:
        file_type = request.form.get('type')
        file = request.files.get('file')  ##获取前端传输来的索引为file的文件
        file_name = ranstr(8) + '.' + file_type
        predict_file = os.path.join(current_app.config["MODEL_PATH"], file_name)
        file.save(predict_file)
        (filepath, predictfilename) = os.path.split(predict_file)
        # 2、读取当前目录下的数据
        data_abs_path = predict_file
        data = None
    logging.info(predict_data,exc_info=True)
    try:
        if task_type == 'matlab':
            # 解压并获取脚本路径和模型路径
            matlab_file, model_file = PredictOneLoadData.get_local_data(model_abs_path, task_type=task_type)
        elif task_type in ['1001', '1002', '1003', '1004', '1005', '1006', 'sklearn']:
            # 读取文件整合在utils.loaddata，所以需要添加判别类型参数
            data, file_name_df = PredictOneLoadData.get_local_data(predict_data, encoding='utf-8',task_type=task_type)
        else:
            # 读取文件整合在utils.loaddata，所以需要添加判别类型参数
            data, file_name_df = PredictOneLoadData.get_local_data(data_abs_path, encoding='utf-8',task_type=task_type)
        logging.info(data,exc_info=True)
    except Exception as e:
        logging.error(e, exc_info=True)
        res = ResMsg(code=ResponseCode.ReadResourceFail, data="Loaddata预测数据读取失败")
        return res.data


    if task_type == 'matlab':
        # eng = matlab.engine.start_matlab()
        # # 进入.m文件目录
        # matlab_dir, matlab_file_name = os.path.split(matlab_file)
        # eng.cd(matlab_dir, nargout=0)
        # # 执行.m文件
        # # result_abs_path = eng.triarea('RF.mat', 'test.csv')
        # result_abs_path = eng.triarea(model_file, data_abs_path)
        # eng.quit()
        matlab_dir, matlab_file_name = os.path.split(matlab_file)
        matlab_name = os.path.splitext(matlab_file_name)[0]
        result_file_name = str(random.randint(0,1000)) + str(task_id) + '.csv'
        result_abs_path = filepath + "/" + result_file_name
        # cmd1 = "cd /opt/zjw/matlab"
        # cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"triarea('RF.mat','/opt/zjw/testdata.csv','/opt/zjw/result.csv')\"
        cmd1 = "cd "+matlab_dir
        cmd2 = "matlab -nojvm -nodisplay -nodesktop -r \"" + matlab_name + "('" + os.path.basename(model_file) + "','" + os.path.abspath(data_abs_path) + "','" + os.path.abspath(result_abs_path) + "')\""
        cmd = cmd1 + "&&" + cmd2
        flag = os.system(cmd)
        if flag != 0:
            logging.error("matlab预测脚本执行失败", exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式和脚本文件"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            return res.data
    else:
        # 3、进行预测
        try:
            predict_result = current_app.extensions["machine_state"].predictOnetask(task_id=task_id, predict_params=data)
        except PredictParamsFormatError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIDataFormatError, data=e.message)
            return res.data
        except TaskNotFoundError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPINotExist, data=e.message)
            return res.data
        except PredictParamsNotFoundDiscreteError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictParamsNotFoundDiscrete, data=e.message)
            return res.data
        except PredcitError as e:
            logging.error(e.message, exc_info=True)
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=e.message)
            return res.data
        except Exception as e:
            logging.error(e, exc_info=True)
            message = "任务Id: " + str(task_id) + " 预测失败, 请检查预测数据格式,或重新部署模型"
            res = ResMsg(code=ResponseCode.PredictAPIFailed, data=message)
            return res.data


        # 4、结果写入本地——输出文件
        if task_type in ['1001','1002','1003','1004','1005','1006','sklearn']:
            data['result'] = predict_result

        elif task_type in ['1007','1008']:   #1007和1008输出的是文件名+result
            file_name_df['result'] = predict_result



    if task_type in ['1001', '1002', '1003', '1004', '1005', '1006', 'sklearn']:
        logging.info(data.values)
        return predict_result
    elif task_type in ['1007', '1008']:  # 1007和1008输出的是文件名+result
        logging.info(file_name_df.values)
        return predict_result
    elif task_type in ('1009', '1010'):
        logging.info(predict_result.values)
        return predict_result
    elif task_type == 'matlab':
        predict_result = pd.read_csv(result_abs_path,header=None)
        logging.info(predict_result.values)
        return json.dumps({'result':predict_result.values.tolist()},ensure_ascii=False)
